{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Python入门(下)</h1>\n",
    "\n",
    "1. [简介](#简介)\n",
    "\n",
    "2. [函数](#函数)<br>\n",
    "   [1. 函数的定义](#函数的定义)<br>\n",
    "   [2. 函数的调用](#函数的调用)<br>\n",
    "   [3. 函数文档](#函数文档)<br>\n",
    "   [4. 函数参数](#函数参数)<br>\n",
    "   [5. 函数的返回值](#函数的返回值)<br>\n",
    "   [6. 变量作用域](#变量作用域)<br>\n",
    " \n",
    "3. [Lambda-表达式](#Lambda-表达式)<br>\n",
    "   [1. 匿名函数的定义](#匿名函数的定义)<br>\n",
    "   [2. 匿名函数的应用](#匿名函数的应用)<br>\n",
    "   \n",
    "4. [类与对象](#类与对象)<br>\n",
    "   [1. 属性和方法组成对象](#属性和方法组成对象)<br>\n",
    "   [2. self是什么？](#self-是什么？)<br>\n",
    "   [3. Python的魔法方法](#Python-的魔法方法)<br>\n",
    "   [4. 公有和私有](#公有和私有)<br>\n",
    "   [5. 继承](#继承)<br>\n",
    "   [6. 组合](#组合)<br>\n",
    "   [7. 类、类对象和实例对象](#类、类对象和实例对象)<br>\n",
    "   [8. 什么是绑定？](#什么是绑定？)<br>\n",
    "   [9. 一些相关的内置函数（BIF）](#一些相关的内置函数（BIF）)<br>\n",
    "   \n",
    "5. [魔法方法](#魔法方法)<br>\n",
    "   [1. 基本的魔法方法](#基本的魔法方法)<br>\n",
    "   [2. 算术运算符](#算术运算符)<br>\n",
    "   [3. 反算术运算符](#反算术运算符)<br>\n",
    "   [4. 增量赋值运算](#增量赋值运算符)<br>\n",
    "   [5. 一元运算符](#一元运算符)<br>\n",
    "   [6. 属性访问](#属性访问)<br>\n",
    "   [7. 描述符](#描述符)<br>\n",
    "   [8. 定制序列](#定制序列)<br>\n",
    "   [9. 迭代器](#迭代器)<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 简介\n",
    "\n",
    "Python 是一种通用编程语言，其在科学计算和机器学习领域具有广泛的应用。\n",
    "\n",
    "本笔记基于阿里云天池龙珠计划 - Python 训练营教程，仅供学习使用。\n",
    "\n",
    "\n",
    "本实验包括以下内容：\n",
    "\n",
    "- 函数\n",
    "    - 函数的定义\n",
    "    - 函数的调用\n",
    "    - 函数文档\n",
    "    - 函数参数\n",
    "    - 函数的返回值\n",
    "    - 变量作用域\n",
    "- Lambda 表达式\n",
    "    - 匿名函数的定义\n",
    "    - 匿名函数的应用\n",
    "- 类与对象\n",
    "    - 对象 = 属性 + 方法\n",
    "    - self 是什么？\n",
    "    - Python 的魔法方法\n",
    "    - 公有和私有\n",
    "    - 继承\n",
    "    - 组合\n",
    "    - 类、类对象和实例对象\n",
    "    - 什么是绑定？\n",
    "    - 一些相关的内置函数（BIF）\n",
    "- 魔法方法\n",
    "    - 基本的魔法方法\n",
    "    - 算术运算符\n",
    "    - 反算术运算符\n",
    "    - 增量赋值运算符\n",
    "    - 一元运算符\n",
    "    - 属性访问\n",
    "    - 描述符\n",
    "    - 定制序列\n",
    "    - 迭代器\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 函数\n",
    "\n",
    "## 函数的定义\n",
    "\n",
    "还记得 Python 里面“万物皆对象”么？Python 把函数也当成对象，可以从另一个函数中返回出来而去构建高阶函数，比如：\n",
    "参数是函数、返回值是函数。\n",
    "\n",
    "我们首先来介绍函数的定义。\n",
    "\n",
    "- 函数以`def`关键词开头，后接函数名和圆括号()。\n",
    "- 函数执行的代码以冒号起始，并且缩进。\n",
    "- return [表达式] 结束函数，选择性地返回一个值给调用方。不带表达式的return相当于返回`None`。\n",
    "\n",
    "\n",
    "\n",
    "> def functionname (parameters):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp; function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp; return [expression]\n",
    "\n",
    "\n",
    "\n",
    "## 函数的调用\n",
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我要调用用户自定义函数!\n",
      "再次调用同一函数\n",
      "hello\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "def printme(str):\n",
    "    print(str)\n",
    "\n",
    "\n",
    "printme(\"我要调用用户自定义函数!\")  # 我要调用用户自定义函数!\n",
    "printme(\"再次调用同一函数\")  # 再次调用同一函数\n",
    "temp = printme('hello') # hello\n",
    "print(temp)  # None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 函数文档"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "传递进来的老马的程序人生叫做实参，因为Ta是具体的参数值！\n",
      "函数定义过程中name是形参\n",
      "Help on function MyFirstFunction in module __main__:\n",
      "\n",
      "MyFirstFunction(name)\n",
      "    函数定义过程中name是形参\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def MyFirstFunction(name):\n",
    "    \"函数定义过程中name是形参\"\n",
    "    # 因为Ta只是一个形式，表示占据一个参数位置\n",
    "    print('传递进来的{0}叫做实参，因为Ta是具体的参数值！'.format(name))\n",
    "\n",
    "\n",
    "MyFirstFunction('老马的程序人生')  \n",
    "# 传递进来的老马的程序人生叫做实参，因为Ta是具体的参数值！\n",
    "\n",
    "print(MyFirstFunction.__doc__)  \n",
    "# 函数定义过程中name是形参\n",
    "\n",
    "help(MyFirstFunction)\n",
    "# Help on function MyFirstFunction in module __main__:\n",
    "# MyFirstFunction(name)\n",
    "#    函数定义过程中name是形参"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 函数参数\n",
    "\n",
    "Python 的函数具有非常灵活多样的参数形态，既可以实现简单的调用，又可以传入非常复杂的参数。从简到繁的参数形态如下：\n",
    "- 位置参数 (positional argument)\n",
    "- 默认参数 (default argument)\n",
    "- 可变参数 (variable argument)\n",
    "- 关键字参数 (keyword argument)\n",
    "- 命名关键字参数 (name keyword argument)\n",
    "- 参数组合\n",
    "\n",
    "\n",
    "**1. 位置参数**\n",
    "\n",
    "> def functionname(arg1):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;return [expression]<br>\n",
    "\n",
    "- `arg1` - 位置参数 ，这些参数在调用函数 (call function) 时位置要固定。\n",
    "\n",
    "**2. 默认参数**\n",
    "\n",
    "\n",
    "> def functionname(arg1, arg2=v):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;return [expression]<br>\n",
    "\n",
    "- `arg2 = v` - 默认参数 = 默认值，调用函数时，默认参数的值如果没有传入，则被认为是默认值。\n",
    "- 默认参数一定要放在位置参数 <b>后面</b>，不然程序会报错。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name:小马,Age:8\n",
      "Name:小马,Age:10\n"
     ]
    }
   ],
   "source": [
    "def printinfo(name, age=8):\n",
    "    print('Name:{0},Age:{1}'.format(name, age))\n",
    "\n",
    "\n",
    "printinfo('小马')  # Name:小马,Age:8\n",
    "printinfo('小马', 10)  # Name:小马,Age:10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- Python 允许函数调用时参数的顺序与声明时不一致，因为 Python 解释器能够用参数名匹配参数值。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name:小马,Age:8\n"
     ]
    }
   ],
   "source": [
    "def printinfo(name, age):\n",
    "    print('Name:{0},Age:{1}'.format(name, age))\n",
    "\n",
    "\n",
    "printinfo(age=8, name='小马')  # Name:小马,Age:8"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "**3. 可变参数**\n",
    "\n",
    "顾名思义，可变参数就是传入的参数个数是可变的，可以是 0, 1, 2 到任意个，是不定长的参数。\n",
    "\n",
    "\n",
    "> def functionname(arg1, arg2=v, *args):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;return [expression]<br>\n",
    "\n",
    "- `*args` - 可变参数，可以是从零个到任意个，自动组装成元组。\n",
    "- 加了星号（*）的变量名会存放所有未命名的变量参数。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "70\n",
      "60\n",
      "50\n"
     ]
    }
   ],
   "source": [
    "def printinfo(arg1, *args):\n",
    "    print(arg1)\n",
    "    for var in args:\n",
    "        print(var)\n",
    "\n",
    "\n",
    "printinfo(10)  # 10\n",
    "printinfo(70, 60, 50)\n",
    "# 70\n",
    "# 60\n",
    "# 50"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "**4. 关键字参数**\n",
    "\n",
    "\n",
    "> def functionname(arg1, arg2=v, *args, **kw):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;return [expression]<br>\n",
    "\n",
    "- `**kw` - 关键字参数，可以是从零个到任意个，自动组装成字典。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "70\n",
      "(60, 50)\n",
      "{}\n",
      "70\n",
      "(60, 50)\n",
      "{'a': 1, 'b': 2}\n"
     ]
    }
   ],
   "source": [
    "def printinfo(arg1, *args, **kwargs):\n",
    "    print(arg1)\n",
    "    print(args)\n",
    "    print(kwargs)\n",
    "\n",
    "\n",
    "printinfo(70, 60, 50)\n",
    "# 70\n",
    "# (60, 50)\n",
    "# {}\n",
    "printinfo(70, 60, 50, a=1, b=2)\n",
    "# 70\n",
    "# (60, 50)\n",
    "# {'a': 1, 'b': 2}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "「可变参数」和「关键字参数」的同异总结如下：\n",
    "- 可变参数允许传入零个到任意个参数，它们在函数调用时自动组装为一个元组 (tuple)。\n",
    "- 关键字参数允许传入零个到任意个参数，它们在函数内部自动组装为一个字典 (dict)。\n",
    "\n",
    "**5. 命名关键字参数**\n",
    "\n",
    "> def functionname(arg1, arg2=v, *args, *, nkw, **kw):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;\"函数_文档字符串\"<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;function_suite<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;return [expression]<br>\n",
    "\n",
    "- `*, nkw` - 命名关键字参数，用户想要输入的关键字参数，定义方式是在nkw 前面加个分隔符 `*`。\n",
    "- 如果要限制关键字参数的名字，就可以用「命名关键字参数」\n",
    "- 使用命名关键字参数时，要特别注意不能缺少参数名。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "70\n",
      "10\n",
      "{'a': 1, 'b': 2}\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "printinfo() takes 1 positional argument but 2 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-7-9267de888e52>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[0;31m# {'a': 1, 'b': 2}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0mprintinfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m70\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     13\u001b[0m \u001b[0;31m# TypeError: printinfo() takes 1 positional argument but 2 were given\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: printinfo() takes 1 positional argument but 2 were given"
     ]
    }
   ],
   "source": [
    "def printinfo(arg1, *, nkw, **kwargs):\n",
    "    print(arg1)\n",
    "    print(nkw)\n",
    "    print(kwargs)\n",
    "\n",
    "\n",
    "printinfo(70, nkw=10, a=1, b=2)\n",
    "# 70\n",
    "# 10\n",
    "# {'a': 1, 'b': 2}\n",
    "\n",
    "printinfo(70, 10, a=1, b=2)\n",
    "# TypeError: printinfo() takes 1 positional argument but 2 were given"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 没有写参数名`nwk`，因此 10 被当成「位置参数」，而原函数只有 1 个位置参数，现在调用了 2 个，因此程序会报错。\n",
    "\n",
    "\n",
    "**6. 参数组合**\n",
    "\n",
    "在 Python 中定义函数，可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数，这 5 种参数中的 4 个都可以一起使用，但是注意，参数定义的顺序必须是：\n",
    "- 位置参数、默认参数、可变参数和关键字参数。\n",
    "- 位置参数、默认参数、命名关键字参数和关键字参数。\n",
    "\n",
    "要注意定义可变参数和关键字参数的语法：\n",
    "- `*args` 是可变参数，`args` 接收的是一个 `tuple`\n",
    "- `**kw` 是关键字参数，`kw` 接收的是一个 `dict`\n",
    "\n",
    "命名关键字参数是为了限制调用者可以传入的参数名，同时可以提供默认值。定义命名关键字参数不要忘了写分隔符 `*`，否则定义的是位置参数。\n",
    "\n",
    "警告：虽然可以组合多达 5 种参数，但不要同时使用太多的组合，否则函数很难懂。\n",
    "\n",
    "\n",
    "## 函数的返回值\n",
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "[1, 2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "def add(a, b):\n",
    "    return a + b\n",
    "\n",
    "\n",
    "print(add(1, 2))  # 3\n",
    "print(add([1, 2, 3], [4, 5, 6]))  # [1, 2, 3, 4, 5, 6]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, '小马的程序人生', 3.14]\n"
     ]
    }
   ],
   "source": [
    "def back():\n",
    "    return [1, '小马的程序人生', 3.14]\n",
    "\n",
    "\n",
    "print(back())  # [1, '小马的程序人生', 3.14]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, '小马的程序人生', 3.14)\n"
     ]
    }
   ],
   "source": [
    "def back():\n",
    "    return 1, '小马的程序人生', 3.14\n",
    "\n",
    "\n",
    "print(back())  # (1, '小马的程序人生', 3.14)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n",
      "None\n",
      "<class 'NoneType'>\n"
     ]
    }
   ],
   "source": [
    "def printme(str):\n",
    "    print(str)\n",
    "\n",
    "temp = printme('hello') # hello\n",
    "print(temp) # None\n",
    "print(type(temp))  # <class 'NoneType'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 变量作用域\n",
    "\n",
    "- Python 中，程序的变量并不是在哪个位置都可以访问的，访问权限决定于这个变量是在哪里赋值的。\n",
    "- 定义在函数内部的变量拥有局部作用域，该变量称为局部变量。\n",
    "- 定义在函数外部的变量拥有全局作用域，该变量称为全局变量。\n",
    "- 局部变量只能在其被声明的函数内部访问，而全局变量可以在整个程序范围内访问。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入原价: 98\n",
      "请输入折扣率: 0.9\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "打折后价格是:88.20\n"
     ]
    }
   ],
   "source": [
    "def discounts(price, rate):\n",
    "    final_price = price * rate\n",
    "    return final_price\n",
    "\n",
    "\n",
    "old_price = float(input('请输入原价:'))  # 98\n",
    "rate = float(input('请输入折扣率:'))  # 0.9\n",
    "new_price = discounts(old_price, rate)\n",
    "print('打折后价格是:%.2f' % new_price)  # 88.20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 当内部作用域想修改外部作用域的变量时，就要用到`global`和`nonlocal`关键字了。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "123\n",
      "123\n"
     ]
    }
   ],
   "source": [
    "num = 1\n",
    "\n",
    "\n",
    "def fun1():\n",
    "    global num  # 需要使用 global 关键字声明\n",
    "    print(num)  # 1\n",
    "    num = 123\n",
    "    print(num)  # 123\n",
    "\n",
    "\n",
    "fun1()\n",
    "print(num)  # 123"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**内嵌函数**\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "outer函数在这被调用\n",
      "inner函数在这被调用\n"
     ]
    }
   ],
   "source": [
    "def outer():\n",
    "    print('outer函数在这被调用')\n",
    "\n",
    "    def inner():\n",
    "        print('inner函数在这被调用')\n",
    "\n",
    "    inner()  # 该函数只能在outer函数内部被调用\n",
    "\n",
    "\n",
    "outer()\n",
    "# outer函数在这被调用\n",
    "# inner函数在这被调用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**闭包**\n",
    "\n",
    "- 是函数式编程的一个重要的语法结构，是一种特殊的内嵌函数。\n",
    "- 如果在一个内部函数里对外层非全局作用域的变量进行引用，那么内部函数就被认为是闭包。\n",
    "- 通过闭包可以访问外层非全局作用域的变量，这个作用域称为 <b>闭包作用域</b>。\n",
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'function'>\n",
      "40\n"
     ]
    }
   ],
   "source": [
    "def funX(x):\n",
    "    def funY(y):\n",
    "        return x * y\n",
    "\n",
    "    return funY\n",
    "\n",
    "\n",
    "i = funX(8)\n",
    "print(type(i))  # <class 'function'>\n",
    "print(i(5))  # 40"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】闭包的返回值通常是函数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "2\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "def make_counter(init):\n",
    "    counter = [init]\n",
    "\n",
    "    def inc(): counter[0] += 1\n",
    "\n",
    "    def dec(): counter[0] -= 1\n",
    "\n",
    "    def get(): return counter[0]\n",
    "\n",
    "    def reset(): counter[0] = init\n",
    "\n",
    "    return inc, dec, get, reset\n",
    "\n",
    "\n",
    "inc, dec, get, reset = make_counter(0)\n",
    "inc()\n",
    "inc()\n",
    "inc()\n",
    "print(get())  # 3\n",
    "dec()\n",
    "print(get())  # 2\n",
    "reset()\n",
    "print(get())  # 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】 如果要修改闭包作用域中的变量则需要 `nonlocal` 关键字"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "def outer():\n",
    "    num = 10\n",
    "\n",
    "    def inner():\n",
    "        nonlocal num  # nonlocal关键字声明\n",
    "        num = 100\n",
    "        print(num)\n",
    "\n",
    "    inner()\n",
    "    print(num)\n",
    "\n",
    "\n",
    "outer()\n",
    "\n",
    "# 100\n",
    "# 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**递归**\n",
    "\n",
    "- 如果一个函数在内部调用自身本身，这个函数就是递归函数。\n",
    "\n",
    "【例子】`n! = 1 x 2 x 3 x ... x n`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120\n",
      "120\n"
     ]
    }
   ],
   "source": [
    "# 利用循环\n",
    "n = 5\n",
    "for k in range(1, 5):\n",
    "    n = n * k\n",
    "print(n)  # 120\n",
    "\n",
    "# 利用递归\n",
    "def factorial(n):\n",
    "    if n == 1:\n",
    "        return 1\n",
    "    return n * factorial(n - 1)\n",
    "\n",
    "\n",
    "print(factorial(5)) # 120"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】斐波那契数列 `f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]\n",
      "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]\n"
     ]
    }
   ],
   "source": [
    "# 利用循环\n",
    "i = 0\n",
    "j = 1\n",
    "lst = list([i, j])\n",
    "for k in range(2, 11):\n",
    "    k = i + j\n",
    "    lst.append(k)\n",
    "    i = j\n",
    "    j = k\n",
    "print(lst)  \n",
    "# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]\n",
    "\n",
    "# 利用递归\n",
    "def recur_fibo(n):\n",
    "    if n <= 1:\n",
    "        return n\n",
    "    return recur_fibo(n - 1) + recur_fibo(n - 2)\n",
    "\n",
    "\n",
    "lst = list()\n",
    "for k in range(11):\n",
    "    lst.append(recur_fibo(k))\n",
    "print(lst)  \n",
    "# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "【例子】设置递归的层数，Python默认递归层数为 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "\n",
    "sys.setrecursionlimit(1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# Lambda 表达式\n",
    "\n",
    "## 匿名函数的定义\n",
    "\n",
    "在 Python 里有两类函数：\n",
    "- 第一类：用 `def` 关键词定义的正规函数\n",
    "- 第二类：用 `lambda` 关键词定义的匿名函数\n",
    "\n",
    "Python 使用 `lambda` 关键词来创建匿名函数，而非`def`关键词，它没有函数名，其语法结构如下：\n",
    "\n",
    "> lambda argument_list: expression\n",
    "\n",
    "- `lambda` - 定义匿名函数的关键词。\n",
    "- `argument_list` - 函数参数，它们可以是位置参数、默认参数、关键字参数，和正规函数里的参数类型一样。\n",
    "- `:`- 冒号，在函数参数和表达式中间要加个冒号。\n",
    "- `expression` - 只是一个表达式，输入函数参数，输出一些值。\n",
    "\n",
    "注意：\n",
    "- `expression` 中没有 return 语句，因为 lambda 不需要它来返回，表达式本身结果就是返回值。\n",
    "- 匿名函数拥有自己的命名空间，且不能访问自己参数列表之外或全局命名空间里的参数。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function sqr at 0x7f79dc21d9d8>\n",
      "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
      "<function <lambda> at 0x7f79dc1e10d0>\n",
      "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
      "30\n",
      "15\n"
     ]
    }
   ],
   "source": [
    "def sqr(x):\n",
    "    return x ** 2\n",
    "\n",
    "\n",
    "print(sqr)\n",
    "# <function sqr at 0x000000BABD3A4400>\n",
    "\n",
    "y = [sqr(x) for x in range(10)]\n",
    "print(y)\n",
    "# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
    "\n",
    "lbd_sqr = lambda x: x ** 2\n",
    "print(lbd_sqr)\n",
    "# <function <lambda> at 0x000000BABB6AC1E0>\n",
    "\n",
    "y = [lbd_sqr(x) for x in range(10)]\n",
    "print(y)\n",
    "# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
    "\n",
    "\n",
    "sumary = lambda arg1, arg2: arg1 + arg2\n",
    "print(sumary(10, 20))  # 30\n",
    "\n",
    "func = lambda *args: sum(args)\n",
    "print(func(1, 2, 3, 4, 5))  # 15"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 匿名函数的应用\n",
    "\n",
    "函数式编程 是指代码中每一块都是不可变的，都由纯函数的形式组成。这里的纯函数，是指函数本身相互独立、互不影响，对于相同的输入，总会有相同的输出，没有任何副作用。\n",
    "\n",
    "【例子】非函数式编程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[11, 12, 13]\n"
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    for i in range(0, len(x)):\n",
    "        x[i] += 10\n",
    "    return x\n",
    "\n",
    "\n",
    "x = [1, 2, 3]\n",
    "f(x)\n",
    "print(x)\n",
    "# [11, 12, 13]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】函数式编程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    y = []\n",
    "    for item in x:\n",
    "        y.append(item + 10)\n",
    "    return y\n",
    "\n",
    "\n",
    "x = [1, 2, 3]\n",
    "f(x)\n",
    "print(x)\n",
    "# [1, 2, 3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "匿名函数 常常应用于函数式编程的高阶函数 (high-order function)中，主要有两种形式：\n",
    "- 参数是函数 (filter, map)\n",
    "- 返回值是函数 (closure)\n",
    "\n",
    "\n",
    "如，在 `filter`和`map`函数中的应用：\n",
    "\n",
    "- `filter(function, iterable)` 过滤序列，过滤掉不符合条件的元素，返回一个迭代器对象，如果要转换为列表，可以使用 `list()` 来转换。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 3, 5, 7, 9]\n"
     ]
    }
   ],
   "source": [
    "odd = lambda x: x % 2 == 1\n",
    "templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "print(list(templist))  # [1, 3, 5, 7, 9]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `map(function, *iterables)` 根据提供的函数对指定序列做映射。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 4, 9, 16, 25]\n",
      "[3, 7, 11, 15, 19]\n"
     ]
    }
   ],
   "source": [
    "m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])\n",
    "print(list(m1))  \n",
    "# [1, 4, 9, 16, 25]\n",
    "\n",
    "m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])\n",
    "print(list(m2))  \n",
    "# [3, 7, 11, 15, 19]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "除了 Python 这些内置函数，我们也可以自己定义高阶函数。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "15\n",
      "5\n",
      "3.0\n"
     ]
    }
   ],
   "source": [
    "def apply_to_list(fun, some_list):\n",
    "    return fun(some_list)\n",
    "\n",
    "lst = [1, 2, 3, 4, 5]\n",
    "print(apply_to_list(sum, lst))\n",
    "# 15\n",
    "\n",
    "print(apply_to_list(len, lst))\n",
    "# 5\n",
    "\n",
    "print(apply_to_list(lambda x: sum(x) / len(x), lst))\n",
    "# 3.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "# 类与对象\n",
    "\n",
    "## 对象 = 属性 + 方法\n",
    "\n",
    "对象是类的实例。换句话说，类主要定义对象的结构，然后我们以类为模板创建对象。类不但包含方法定义，而且还包含所有实例共享的数据。\n",
    "\n",
    "- 封装：信息隐蔽技术\n",
    "\n",
    "我们可以使用关键字 `class` 定义 Python 类，关键字后面紧跟类的名称、分号和类的实现。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Turtle object at 0x7f79dc1f34a8>\n",
      "<class '__main__.Turtle'>\n",
      "<class '__main__.Turtle'>\n",
      "Turtle\n",
      "我正在很努力的向前爬...\n",
      "我正在飞快的向前跑...\n",
      "咬死你咬死你!!\n",
      "<class 'type'>\n"
     ]
    }
   ],
   "source": [
    "class Turtle:  # Python中的类名约定以大写字母开头\n",
    "    \"\"\"关于类的一个简单例子\"\"\"\n",
    "    # 属性\n",
    "    color = 'green'\n",
    "    weight = 10\n",
    "    legs = 4\n",
    "    shell = True\n",
    "    mouth = '大嘴'\n",
    "\n",
    "    # 方法\n",
    "    def climb(self):\n",
    "        print('我正在很努力的向前爬...')\n",
    "\n",
    "    def run(self):\n",
    "        print('我正在飞快的向前跑...')\n",
    "\n",
    "    def bite(self):\n",
    "        print('咬死你咬死你!!')\n",
    "\n",
    "    def eat(self):\n",
    "        print('有得吃，真满足...')\n",
    "\n",
    "    def sleep(self):\n",
    "        print('困了，睡了，晚安，zzz')\n",
    "\n",
    "\n",
    "tt = Turtle()\n",
    "print(tt)\n",
    "# <__main__.Turtle object at 0x0000007C32D67F98>\n",
    "\n",
    "print(type(tt))\n",
    "# <class '__main__.Turtle'>\n",
    "\n",
    "print(tt.__class__)\n",
    "# <class '__main__.Turtle'>\n",
    "\n",
    "print(tt.__class__.__name__)\n",
    "# Turtle\n",
    "\n",
    "tt.climb()\n",
    "# 我正在很努力的向前爬...\n",
    "\n",
    "tt.run()\n",
    "# 我正在飞快的向前跑...\n",
    "\n",
    "tt.bite()\n",
    "# 咬死你咬死你!!\n",
    "\n",
    "# Python类也是对象。它们是type的实例\n",
    "print(type(Turtle))\n",
    "# <class 'type'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 继承：子类自动共享父类之间数据和方法的机制\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 5, 7, 8, 9]\n"
     ]
    }
   ],
   "source": [
    "class MyList(list):\n",
    "    pass\n",
    "\n",
    "\n",
    "lst = MyList([1, 5, 2, 7, 8])\n",
    "lst.append(9)\n",
    "lst.sort()\n",
    "print(lst)\n",
    "\n",
    "# [1, 2, 5, 7, 8, 9]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- 多态：不同对象对同一方法响应不同的行动\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pig is walking\n"
     ]
    }
   ],
   "source": [
    "class Animal:\n",
    "    def run(self):\n",
    "        raise AttributeError('子类必须实现这个方法')\n",
    "\n",
    "\n",
    "class People(Animal):\n",
    "    def run(self):\n",
    "        print('人正在走')\n",
    "\n",
    "\n",
    "class Pig(Animal):\n",
    "    def run(self):\n",
    "        print('pig is walking')\n",
    "\n",
    "\n",
    "class Dog(Animal):\n",
    "    def run(self):\n",
    "        print('dog is running')\n",
    "\n",
    "\n",
    "def func(animal):\n",
    "    animal.run()\n",
    "\n",
    "\n",
    "func(Pig())\n",
    "# pig is walking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "## self 是什么？\n",
    "\n",
    "Python 的 `self` 相当于 C++ 的 `this` 指针。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Test object at 0x7f79dc23af98>\n",
      "<class '__main__.Test'>\n"
     ]
    }
   ],
   "source": [
    "class Test:\n",
    "    def prt(self):\n",
    "        print(self)\n",
    "        print(self.__class__)\n",
    "\n",
    "\n",
    "t = Test()\n",
    "t.prt()\n",
    "# <__main__.Test object at 0x000000BC5A351208>\n",
    "# <class '__main__.Test'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "类的方法与普通的函数只有一个特别的区别 —— 它们必须有一个额外的第一个参数名称（对应于该实例，即该对象本身），按照惯例它的名称是 `self`。在调用方法时，我们无需明确提供与参数 `self` 相对应的参数。\n",
    "\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫球A,该死的，谁踢我...\n",
      "我叫球B,该死的，谁踢我...\n"
     ]
    }
   ],
   "source": [
    "class Ball:\n",
    "    def setName(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def kick(self):\n",
    "        print(\"我叫%s,该死的，谁踢我...\" % self.name)\n",
    "\n",
    "\n",
    "a = Ball()\n",
    "a.setName(\"球A\")\n",
    "b = Ball()\n",
    "b.setName(\"球B\")\n",
    "c = Ball()\n",
    "c.setName(\"球C\")\n",
    "a.kick()\n",
    "# 我叫球A,该死的，谁踢我...\n",
    "b.kick()\n",
    "# 我叫球B,该死的，谁踢我..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "## Python 的魔法方法\n",
    "\n",
    "据说，Python 的对象天生拥有一些神奇的方法，它们是面向对象的 Python 的一切...\n",
    "\n",
    "它们是可以给你的类增加魔力的特殊方法...\n",
    "\n",
    "如果你的对象实现了这些方法中的某一个，那么这个方法就会在特殊的情况下被 Python 所调用，而这一切都是自动发生的...\n",
    "\n",
    "类有一个名为`__init__(self[, param1, param2...])`的魔法方法，该方法在类实例化时会自动调用。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫球A,该死的，谁踢我...\n",
      "我叫球B,该死的，谁踢我...\n"
     ]
    }
   ],
   "source": [
    "\n",
    "class Ball:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def kick(self):\n",
    "        print(\"我叫%s,该死的，谁踢我...\" % self.name)\n",
    "\n",
    "\n",
    "a = Ball(\"球A\")\n",
    "b = Ball(\"球B\")\n",
    "c = Ball(\"球C\")\n",
    "a.kick()\n",
    "# 我叫球A,该死的，谁踢我...\n",
    "b.kick()\n",
    "# 我叫球B,该死的，谁踢我..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "## 公有和私有\n",
    "\n",
    "在 Python 中定义私有变量只需要在变量名或函数名前加上“__”两个下划线，那么这个函数或变量就会为私有的了。\n",
    "\n",
    "【例子】类的私有属性实例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "2\n",
      "2\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'JustCounter' object has no attribute '__secretCount'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-34-0e7487766f54>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     16\u001b[0m \u001b[0;31m# Python的私有为伪私有\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     17\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_JustCounter__secretCount\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__secretCount\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     19\u001b[0m \u001b[0;31m# AttributeError: 'JustCounter' object has no attribute '__secretCount'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'JustCounter' object has no attribute '__secretCount'"
     ]
    }
   ],
   "source": [
    "class JustCounter:\n",
    "    __secretCount = 0  # 私有变量\n",
    "    publicCount = 0  # 公开变量\n",
    "\n",
    "    def count(self):\n",
    "        self.__secretCount += 1\n",
    "        self.publicCount += 1\n",
    "        print(self.__secretCount)\n",
    "\n",
    "\n",
    "counter = JustCounter()\n",
    "counter.count()  # 1\n",
    "counter.count()  # 2\n",
    "print(counter.publicCount)  # 2\n",
    "\n",
    "# Python的私有为伪私有\n",
    "print(counter._JustCounter__secretCount)  # 2 \n",
    "print(counter.__secretCount)  \n",
    "# AttributeError: 'JustCounter' object has no attribute '__secretCount'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "【例子】类的私有方法实例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name  :  老马的程序人生\n",
      "url :  https://blog.csdn.net/LSGO_MYP\n",
      "这是公共方法\n",
      "这是私有方法\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Site' object has no attribute '__foo'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-35-555cf676b575>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     25\u001b[0m \u001b[0;31m# 这是私有方法\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     26\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 27\u001b[0;31m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__foo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     28\u001b[0m \u001b[0;31m# AttributeError: 'Site' object has no attribute '__foo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Site' object has no attribute '__foo'"
     ]
    }
   ],
   "source": [
    "class Site:\n",
    "    def __init__(self, name, url):\n",
    "        self.name = name  # public\n",
    "        self.__url = url  # private\n",
    "\n",
    "    def who(self):\n",
    "        print('name  : ', self.name)\n",
    "        print('url : ', self.__url)\n",
    "\n",
    "    def __foo(self):  # 私有方法\n",
    "        print('这是私有方法')\n",
    "\n",
    "    def foo(self):  # 公共方法\n",
    "        print('这是公共方法')\n",
    "        self.__foo()\n",
    "\n",
    "\n",
    "x = Site('老马的程序人生', 'https://blog.csdn.net/LSGO_MYP')\n",
    "x.who()\n",
    "# name  :  老马的程序人生\n",
    "# url :  https://blog.csdn.net/LSGO_MYP\n",
    "\n",
    "x.foo()\n",
    "# 这是公共方法\n",
    "# 这是私有方法\n",
    "\n",
    "x.__foo()\n",
    "# AttributeError: 'Site' object has no attribute '__foo'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "##  继承\n",
    "\n",
    "Python 同样支持类的继承，派生类的定义如下所示：\n",
    "\n",
    "\n",
    "> class DerivedClassName(BaseClassName):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-1<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-N\n",
    "\n",
    "`BaseClassName`（基类名）必须与派生类定义在一个作用域内。除了类，还可以用表达式，基类定义在另一个模块中时这一点非常有用：\n",
    "\n",
    "> class DerivedClassName(modname.BaseClassName):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-1<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-N\n",
    "\n",
    "\n",
    "【例子】如果子类中定义与父类同名的方法或属性，则会自动覆盖父类对应的方法或属性。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小马的程序人生 说: 我 10 岁了，我在读 3 年级\n"
     ]
    }
   ],
   "source": [
    "# 类定义\n",
    "class People:\n",
    "    # 定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    # 定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "\n",
    "    # 定义构造方法\n",
    "    def __init__(self, n, a, w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" % (self.name, self.age))\n",
    "\n",
    "\n",
    "# 单继承示例\n",
    "class Student(People):\n",
    "    grade = ''\n",
    "\n",
    "    def __init__(self, n, a, w, g):\n",
    "        # 调用父类的构函\n",
    "        people.__init__(self, n, a, w)\n",
    "        self.grade = g\n",
    "\n",
    "    # 覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\" % (self.name, self.age, self.grade))\n",
    "\n",
    "\n",
    "s = Student('小马的程序人生', 10, 60, 3)\n",
    "s.speak()\n",
    "# 小马的程序人生 说: 我 10 岁了，我在读 3 年级"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "注意：如果上面的程序去掉：`people.__init__(self, n, a, w)`，则输出：` 说: 我 0 岁了，我在读 3 年级`，因为子类的构造方法把父类的构造方法覆盖了。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我的位置 0 8\n",
      "吃货的梦想就是天天有得吃！\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Shark' object has no attribute 'x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-40-f795a471252a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     40\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mShark\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     41\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 吃货的梦想就是天天有得吃！\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 42\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     43\u001b[0m \u001b[0;31m# AttributeError: 'Shark' object has no attribute 'x'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-40-f795a471252a>\u001b[0m in \u001b[0;36mmove\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mmove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m-=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"我的位置\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Shark' object has no attribute 'x'"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "class Fish:\n",
    "    def __init__(self):\n",
    "        self.x = random.randint(0, 10)\n",
    "        self.y = random.randint(0, 10)\n",
    "\n",
    "    def move(self):\n",
    "        self.x -= 1\n",
    "        print(\"我的位置\", self.x, self.y)\n",
    "\n",
    "\n",
    "class GoldFish(Fish):  # 金鱼\n",
    "    pass\n",
    "\n",
    "\n",
    "class Carp(Fish):  # 鲤鱼\n",
    "    pass\n",
    "\n",
    "\n",
    "class Salmon(Fish):  # 三文鱼\n",
    "    pass\n",
    "\n",
    "\n",
    "class Shark(Fish):  # 鲨鱼\n",
    "    def __init__(self):\n",
    "        self.hungry = True\n",
    "\n",
    "    def eat(self):\n",
    "        if self.hungry:\n",
    "            print(\"吃货的梦想就是天天有得吃！\")\n",
    "            self.hungry = False\n",
    "        else:\n",
    "            print(\"太撑了，吃不下了！\")\n",
    "            self.hungry = True\n",
    "\n",
    "\n",
    "g = GoldFish()\n",
    "g.move()  # 我的位置 9 4\n",
    "s = Shark()\n",
    "s.eat() # 吃货的梦想就是天天有得吃！\n",
    "s.move()  \n",
    "# AttributeError: 'Shark' object has no attribute 'x'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "解决该问题可用以下两种方式：\n",
    "- 调用未绑定的父类方法`Fish.__init__(self)`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Shark(Fish):  # 鲨鱼\n",
    "    def __init__(self):\n",
    "        Fish.__init__(self)\n",
    "        self.hungry = True\n",
    "\n",
    "    def eat(self):\n",
    "        if self.hungry:\n",
    "            print(\"吃货的梦想就是天天有得吃！\")\n",
    "            self.hungry = False\n",
    "        else:\n",
    "            print(\"太撑了，吃不下了！\")\n",
    "            self.hungry = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- 使用super函数`super().__init__()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Shark(Fish):  # 鲨鱼\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.hungry = True\n",
    "\n",
    "    def eat(self):\n",
    "        if self.hungry:\n",
    "            print(\"吃货的梦想就是天天有得吃！\")\n",
    "            self.hungry = False\n",
    "        else:\n",
    "            print(\"太撑了，吃不下了！\")\n",
    "            self.hungry = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Python 虽然支持多继承的形式，但我们一般不使用多继承，因为容易引起混乱。\n",
    "\n",
    "\n",
    "> class DerivedClassName(Base1, Base2, Base3):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-1<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;statement-N<br>\n",
    "\n",
    "\n",
    "需要注意圆括号中父类的顺序，若是父类中有相同的方法名，而在子类使用时未指定，Python 从左至右搜索，即方法在子类中未找到时，从左到右查找父类中是否包含方法。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫 Tim，我是一个演说家，我演讲的主题是 Python\n",
      "Tim 说: 我 25 岁了，我在读 4 年级\n"
     ]
    }
   ],
   "source": [
    "# 类定义\n",
    "class People:\n",
    "    # 定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    # 定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "\n",
    "    # 定义构造方法\n",
    "    def __init__(self, n, a, w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" % (self.name, self.age))\n",
    "\n",
    "\n",
    "# 单继承示例\n",
    "class Student(People):\n",
    "    grade = ''\n",
    "\n",
    "    def __init__(self, n, a, w, g):\n",
    "        # 调用父类的构函\n",
    "        People.__init__(self, n, a, w)\n",
    "        self.grade = g\n",
    "\n",
    "    # 覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\" % (self.name, self.age, self.grade))\n",
    "\n",
    "\n",
    "# 另一个类，多重继承之前的准备\n",
    "class Speaker:\n",
    "    topic = ''\n",
    "    name = ''\n",
    "\n",
    "    def __init__(self, n, t):\n",
    "        self.name = n\n",
    "        self.topic = t\n",
    "\n",
    "    def speak(self):\n",
    "        print(\"我叫 %s，我是一个演说家，我演讲的主题是 %s\" % (self.name, self.topic))\n",
    "\n",
    "\n",
    "# 多重继承\n",
    "class Sample01(Speaker, Student):\n",
    "    a = ''\n",
    "\n",
    "    def __init__(self, n, a, w, g, t):\n",
    "        Student.__init__(self, n, a, w, g)\n",
    "        Speaker.__init__(self, n, t)\n",
    "\n",
    "# 方法名同，默认调用的是在括号中排前地父类的方法\n",
    "test = Sample01(\"Tim\", 25, 80, 4, \"Python\")\n",
    "test.speak()  \n",
    "# 我叫 Tim，我是一个演说家，我演讲的主题是 Python\n",
    "\n",
    "class Sample02(Student, Speaker):\n",
    "    a = ''\n",
    "\n",
    "    def __init__(self, n, a, w, g, t):\n",
    "        Student.__init__(self, n, a, w, g)\n",
    "        Speaker.__init__(self, n, t)\n",
    "\n",
    "# 方法名同，默认调用的是在括号中排前地父类的方法\n",
    "test = Sample02(\"Tim\", 25, 80, 4, \"Python\")\n",
    "test.speak()  \n",
    "# Tim 说: 我 25 岁了，我在读 4 年级"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 组合\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "水池里面有乌龟2只，小鱼3条\n"
     ]
    }
   ],
   "source": [
    "class Turtle:\n",
    "    def __init__(self, x):\n",
    "        self.num = x\n",
    "\n",
    "\n",
    "class Fish:\n",
    "    def __init__(self, x):\n",
    "        self.num = x\n",
    "\n",
    "\n",
    "class Pool:\n",
    "    def __init__(self, x, y):\n",
    "        self.turtle = Turtle(x)\n",
    "        self.fish = Fish(y)\n",
    "\n",
    "    def print_num(self):\n",
    "        print(\"水池里面有乌龟%s只，小鱼%s条\" % (self.turtle.num, self.fish.num))\n",
    "\n",
    "\n",
    "p = Pool(2, 3)\n",
    "p.print_num()\n",
    "# 水池里面有乌龟2只，小鱼3条"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 类、类对象和实例对象\n",
    "\n",
    "![类对象和实例对象](https://img-blog.csdnimg.cn/20191007090316462.png)\n",
    "\n",
    "类对象：创建一个类，其实也是一个对象也在内存开辟了一块空间，称为类对象，类对象只有一个。\n",
    "\n",
    "> class A(object):<br>\n",
    "> &nbsp; &nbsp; &nbsp; &nbsp;pass\n",
    "\n",
    "实例对象：就是通过实例化类创建的对象，称为实例对象，实例对象可以有多个。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A(object):\n",
    "    pass\n",
    "\n",
    "# 实例化对象 a、b、c都属于实例对象。\n",
    "a = A()\n",
    "b = A()\n",
    "c = A()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "类属性：类里面方法外面定义的变量称为类属性。类属性所属于类对象并且多个实例对象之间共享同一个类属性，说白了就是类属性所有的通过该类实例化的对象都能共享。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A():\n",
    "    a = 0  #类属性\n",
    "    def __init__(self, xx):\n",
    "        A.a = xx  #使用类属性可以通过 （类名.类属性）调用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "实例属性：实例属性和具体的某个实例对象有关系，并且一个实例对象和另外一个实例对象是不共享属性的，说白了实例属性只能在自己的对象里面使用，其他的对象不能直接使用，因为`self`是谁调用，它的值就属于该对象。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "类对象.类属性的值: 100\n",
      "self.类属性的值 100\n",
      "self.实例属性的值 100\n",
      "类对象.类属性的值: 100\n",
      "self.类属性的值 100\n",
      "self.实例属性的值 100\n",
      "类对象.类属性的值: 100\n",
      "self.类属性的值 200\n",
      "self.实例属性的值 200\n",
      "类对象.类属性的值: 100\n",
      "self.类属性的值 100\n",
      "self.实例属性的值 100\n",
      "类对象.类属性的值: 300\n",
      "self.类属性的值 200\n",
      "self.实例属性的值 200\n",
      "类对象.类属性的值: 300\n",
      "self.类属性的值 300\n",
      "self.实例属性的值 100\n"
     ]
    }
   ],
   "source": [
    "# 创建类对象\n",
    "class Test(object):\n",
    "    class_attr = 100  # 类属性\n",
    "\n",
    "    def __init__(self):\n",
    "        self.sl_attr = 100  # 实例属性\n",
    "\n",
    "    def func(self):\n",
    "        print('类对象.类属性的值:', Test.class_attr)  # 调用类属性\n",
    "        print('self.类属性的值', self.class_attr)  # 相当于把类属性 变成实例属性\n",
    "        print('self.实例属性的值', self.sl_attr)  # 调用实例属性\n",
    "\n",
    "\n",
    "a = Test()\n",
    "a.func()\n",
    "\n",
    "# 类对象.类属性的值: 100\n",
    "# self.类属性的值 100\n",
    "# self.实例属性的值 100\n",
    "\n",
    "b = Test()\n",
    "b.func()\n",
    "\n",
    "# 类对象.类属性的值: 100\n",
    "# self.类属性的值 100\n",
    "# self.实例属性的值 100\n",
    "\n",
    "a.class_attr = 200\n",
    "a.sl_attr = 200\n",
    "a.func()\n",
    "\n",
    "# 类对象.类属性的值: 100\n",
    "# self.类属性的值 200\n",
    "# self.实例属性的值 200\n",
    "\n",
    "b.func()\n",
    "\n",
    "# 类对象.类属性的值: 100\n",
    "# self.类属性的值 100\n",
    "# self.实例属性的值 100\n",
    "\n",
    "Test.class_attr = 300\n",
    "a.func()\n",
    "\n",
    "# 类对象.类属性的值: 300\n",
    "# self.类属性的值 200\n",
    "# self.实例属性的值 200\n",
    "\n",
    "b.func()\n",
    "# 类对象.类属性的值: 300\n",
    "# self.类属性的值 300\n",
    "# self.实例属性的值 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "注意：属性与方法名相同，属性会覆盖方法。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x_man\n",
      "1\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "'int' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-48-6db89adc213b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0maa\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maa\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0maa\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     11\u001b[0m \u001b[0;31m# TypeError: 'int' object is not callable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: 'int' object is not callable"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def x(self):\n",
    "        print('x_man')\n",
    "\n",
    "\n",
    "aa = A()\n",
    "aa.x()  # x_man\n",
    "aa.x = 1\n",
    "print(aa.x)  # 1\n",
    "aa.x()\n",
    "# TypeError: 'int' object is not callable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 什么是绑定？\n",
    "\n",
    "Python 严格要求方法需要有实例才能被调用，这种限制其实就是 Python 所谓的绑定概念。\n",
    "\n",
    "Python 对象的数据属性通常存储在名为`.__ dict__`的字典中，我们可以直接访问`__dict__`，或利用 Python 的内置函数`vars()`获取`.__ dict__`。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{}\n",
      "{}\n",
      "{'__module__': '__main__', 'setXY': <function CC.setXY at 0x7f79dc1afbf8>, 'printXY': <function CC.printXY at 0x7f79dc1afb70>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}\n",
      "{'x': 4, 'y': 5}\n",
      "{'__module__': '__main__', 'setXY': <function CC.setXY at 0x7f79dc1afbf8>, 'printXY': <function CC.printXY at 0x7f79dc1afb70>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}\n",
      "{'__module__': '__main__', 'setXY': <function CC.setXY at 0x7f79dc1afbf8>, 'printXY': <function CC.printXY at 0x7f79dc1afb70>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}\n"
     ]
    }
   ],
   "source": [
    "class CC:\n",
    "    def setXY(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def printXY(self):\n",
    "        print(self.x, self.y)\n",
    "\n",
    "\n",
    "dd = CC()\n",
    "print(dd.__dict__)\n",
    "# {}\n",
    "\n",
    "print(vars(dd))\n",
    "# {}\n",
    "\n",
    "print(CC.__dict__)\n",
    "# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY': <function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}\n",
    "\n",
    "dd.setXY(4, 5)\n",
    "print(dd.__dict__)\n",
    "# {'x': 4, 'y': 5}\n",
    "\n",
    "print(vars(CC))\n",
    "# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}\n",
    "\n",
    "print(CC.__dict__)\n",
    "# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 一些相关的内置函数（BIF）\n",
    "- `issubclass(class, classinfo)` 方法用于判断参数 class 是否是类型参数 classinfo 的子类。\n",
    "- 一个类被认为是其自身的子类。\n",
    "- `classinfo`可以是类对象的元组，只要class是其中任何一个候选类的子类，则返回`True`。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    pass\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    pass\n",
    "\n",
    "\n",
    "print(issubclass(B, A))  # True\n",
    "print(issubclass(B, B))  # True\n",
    "print(issubclass(A, B))  # False\n",
    "print(issubclass(B, object))  # True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `isinstance(object, classinfo)` 方法用于判断一个对象是否是一个已知的类型，类似`type()`。\n",
    "- `type()`不会认为子类是一种父类类型，不考虑继承关系。\n",
    "- `isinstance()`会认为子类是一种父类类型，考虑继承关系。\n",
    "- 如果第一个参数不是对象，则永远返回`False`。\n",
    "- 如果第二个参数不是类或者由类对象组成的元组，会抛出一个`TypeError`异常。\n",
    "\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n",
      "True\n",
      "True\n",
      "True\n",
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "a = 2\n",
    "print(isinstance(a, int))  # True\n",
    "print(isinstance(a, str))  # False\n",
    "print(isinstance(a, (str, int, list)))  # True\n",
    "\n",
    "\n",
    "class A:\n",
    "    pass\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    pass\n",
    "\n",
    "\n",
    "print(isinstance(A(), A))  # True\n",
    "print(type(A()) == A)  # True\n",
    "print(isinstance(B(), A))  # True\n",
    "print(type(B()) == A)  # False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `hasattr(object, name)`用于判断对象是否包含对应的属性。\n",
    "\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "class Coordinate:\n",
    "    x = 10\n",
    "    y = -5\n",
    "    z = 0\n",
    "\n",
    "\n",
    "point1 = Coordinate()\n",
    "print(hasattr(point1, 'x'))  # True\n",
    "print(hasattr(point1, 'y'))  # True\n",
    "print(hasattr(point1, 'z'))  # True\n",
    "print(hasattr(point1, 'no'))  # False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `getattr(object, name[, default])`用于返回一个对象属性值。\n",
    "\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "3\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'A' object has no attribute 'bar2'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-53-a381bb20461b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      6\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'bar'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'bar2'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'bar2'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      9\u001b[0m \u001b[0;31m# AttributeError: 'A' object has no attribute 'bar2'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'A' object has no attribute 'bar2'"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    bar = 1\n",
    "\n",
    "\n",
    "a = A()\n",
    "print(getattr(a, 'bar'))  # 1\n",
    "print(getattr(a, 'bar2', 3))  # 3\n",
    "print(getattr(a, 'bar2'))\n",
    "# AttributeError: 'A' object has no attribute 'bar2'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】这个例子很酷！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 1\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    def set(self, a, b):\n",
    "        x = a\n",
    "        a = b\n",
    "        b = x\n",
    "        print(a, b)\n",
    "\n",
    "\n",
    "a = A()\n",
    "c = getattr(a, 'set')\n",
    "c(a='1', b='2')  # 2 1\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `setattr(object, name, value)`对应函数 `getattr()`，用于设置属性值，该属性不一定是存在的。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "5\n",
      "28\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    bar = 1\n",
    "\n",
    "\n",
    "a = A()\n",
    "print(getattr(a, 'bar'))  # 1\n",
    "setattr(a, 'bar', 5)\n",
    "print(a.bar)  # 5\n",
    "setattr(a, \"age\", 28)\n",
    "print(a.age)  # 28"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `delattr(object, name)`用于删除属性。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x =  10\n",
      "y =  -5\n",
      "z =  0\n",
      "--删除 z 属性后--\n",
      "x =  10\n",
      "y =  -5\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Coordinate' object has no attribute 'z'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-57-e10298e6b2b2>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     19\u001b[0m \u001b[0;31m# 触发错误\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'z = '\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpoint1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     21\u001b[0m \u001b[0;31m# AttributeError: 'Coordinate' object has no attribute 'z'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Coordinate' object has no attribute 'z'"
     ]
    }
   ],
   "source": [
    "class Coordinate:\n",
    "    x = 10\n",
    "    y = -5\n",
    "    z = 0\n",
    "\n",
    "\n",
    "point1 = Coordinate()\n",
    "\n",
    "print('x = ', point1.x)  # x =  10\n",
    "print('y = ', point1.y)  # y =  -5\n",
    "print('z = ', point1.z)  # z =  0\n",
    "\n",
    "delattr(Coordinate, 'z')\n",
    "\n",
    "print('--删除 z 属性后--')  # --删除 z 属性后--\n",
    "print('x = ', point1.x)  # x =  10\n",
    "print('y = ', point1.y)  # y =  -5\n",
    "\n",
    "# 触发错误\n",
    "print('z = ', point1.z)\n",
    "# AttributeError: 'Coordinate' object has no attribute 'z'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `class property([fget[, fset[, fdel[, doc]]]])`用于在新式类中返回属性值。\n",
    "    - `fget` -- 获取属性值的函数\n",
    "    - `fset` -- 设置属性值的函数\n",
    "    - `fdel` -- 删除属性值函数\n",
    "    - `doc` -- 属性描述信息\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'C' object has no attribute '_C__x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-58-13f37165d450>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     21\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     22\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mcc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m  \u001b[0;31m# 触发deleter: del cc.x\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 23\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     24\u001b[0m \u001b[0;31m# AttributeError: 'C' object has no attribute '_C__x'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-58-13f37165d450>\u001b[0m in \u001b[0;36mgetx\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mgetx\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__x\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0msetx\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'C' object has no attribute '_C__x'"
     ]
    }
   ],
   "source": [
    "class C(object):\n",
    "    def __init__(self):\n",
    "        self.__x = None\n",
    "\n",
    "    def getx(self):\n",
    "        return self.__x\n",
    "\n",
    "    def setx(self, value):\n",
    "        self.__x = value\n",
    "\n",
    "    def delx(self):\n",
    "        del self.__x\n",
    "\n",
    "    x = property(getx, setx, delx, \"I'm the 'x' property.\")\n",
    "\n",
    "\n",
    "cc = C()\n",
    "cc.x = 2  # 触发setter: cc.x = value\n",
    "print(cc.x)  # 触发getter: cc.x\n",
    "             # 2 \n",
    "\n",
    "del cc.x  # 触发deleter: del cc.x\n",
    "print(cc.x)\n",
    "# AttributeError: 'C' object has no attribute '_C__x'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# 魔法方法\n",
    "\n",
    "魔法方法总是被双下划线包围，例如`__init__`。\n",
    "\n",
    "魔法方法是面向对象的 Python 的一切，如果你不知道魔法方法，说明你还没能意识到面向对象的 Python 的强大。\n",
    "\n",
    "魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用。\n",
    "\n",
    "魔法方法的第一个参数应为`cls`（类方法） 或者`self`（实例方法）。\n",
    "- `cls`：代表一个类的名称\n",
    "- `self`：代表一个实例对象的名称\n",
    "\n",
    "\n",
    "## 基本的魔法方法\n",
    "\n",
    "- `__init__(self[, ...])` 构造器，当一个实例被创建的时候调用的初始化方法\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18\n",
      "20\n"
     ]
    }
   ],
   "source": [
    "class Rectangle:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def getPeri(self):\n",
    "        return (self.x + self.y) * 2\n",
    "\n",
    "    def getArea(self):\n",
    "        return self.x * self.y\n",
    "\n",
    "\n",
    "rect = Rectangle(4, 5)\n",
    "print(rect.getPeri())  # 18\n",
    "print(rect.getArea())  # 20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "- `__new__(cls[, ...])` 在一个对象实例化的时候所调用的第一个方法，在调用`__init__`初始化前，先调用`__new__`。\n",
    "    - `__new__`至少要有一个参数`cls`，代表要实例化的类，此参数在实例化时由 Python 解释器自动提供，后面的参数直接传递给`__init__`。\n",
    "    - `__new__`对当前类进行了实例化，并将实例返回，传给`__init__`的`self`。但是，执行了`__new__`，并不一定会进入`__init__`，只有`__new__`返回了，当前类`cls`的实例，当前类的`__init__`才会进入。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "into B __new__\n",
      "<class '__main__.B'>\n",
      "into A __new__\n",
      "<class '__main__.B'>\n",
      "into B __init__\n",
      "into B __new__\n",
      "<class '__main__.B'>\n",
      "into A __new__\n",
      "<class '__main__.A'>\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    def __init__(self, value):\n",
    "        print(\"into A __init__\")\n",
    "        self.value = value\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        print(\"into A __new__\")\n",
    "        print(cls)\n",
    "        return object.__new__(cls)\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    def __init__(self, value):\n",
    "        print(\"into B __init__\")\n",
    "        self.value = value\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        print(\"into B __new__\")\n",
    "        print(cls)\n",
    "        return super().__new__(cls, *args, **kwargs)\n",
    "\n",
    "\n",
    "b = B(10)\n",
    "\n",
    "# 结果：\n",
    "# into B __new__\n",
    "# <class '__main__.B'>\n",
    "# into A __new__\n",
    "# <class '__main__.B'>\n",
    "# into B __init__\n",
    "\n",
    "class A(object):\n",
    "    def __init__(self, value):\n",
    "        print(\"into A __init__\")\n",
    "        self.value = value\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        print(\"into A __new__\")\n",
    "        print(cls)\n",
    "        return object.__new__(cls)\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    def __init__(self, value):\n",
    "        print(\"into B __init__\")\n",
    "        self.value = value\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        print(\"into B __new__\")\n",
    "        print(cls)\n",
    "        return super().__new__(A, *args, **kwargs)  # 改动了cls变为A\n",
    "\n",
    "\n",
    "b = B(10)\n",
    "\n",
    "# 结果：\n",
    "# into B __new__\n",
    "# <class '__main__.B'>\n",
    "# into A __new__\n",
    "# <class '__main__.A'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- 若`__new__`没有正确返回当前类`cls`的实例，那`__init__`是不会被调用的，即使是父类的实例也不行，将没有`__init__`被调用。\n",
    "\n",
    "【例子】利用`__new__`实现单例模式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139806024322744\n",
      "139806024321960\n",
      "139806024093936\n",
      "139806024093936\n"
     ]
    }
   ],
   "source": [
    "class Earth:\n",
    "    pass\n",
    "\n",
    "\n",
    "a = Earth()\n",
    "print(id(a))  # 260728291456\n",
    "b = Earth()\n",
    "print(id(b))  # 260728291624\n",
    "\n",
    "class Earth:\n",
    "    __instance = None  # 定义一个类属性做判断\n",
    "\n",
    "    def __new__(cls):\n",
    "        if cls.__instance is None:\n",
    "            cls.__instance = object.__new__(cls)\n",
    "            return cls.__instance\n",
    "        else:\n",
    "            return cls.__instance\n",
    "\n",
    "\n",
    "a = Earth()\n",
    "print(id(a))  # 512320401648\n",
    "b = Earth()\n",
    "print(id(b))  # 512320401648"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__new__`方法主要是当你继承一些不可变的 class 时（比如`int, str, tuple`）， 提供给你一个自定义这些类的实例化过程的途径。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I LOVE LSGOGROUP\n"
     ]
    }
   ],
   "source": [
    "class CapStr(str):\n",
    "    def __new__(cls, string):\n",
    "        string = string.upper()\n",
    "        return str.__new__(cls, string)\n",
    "\n",
    "\n",
    "a = CapStr(\"i love lsgogroup\")\n",
    "print(a)  # I LOVE LSGOGROUP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__del__(self)` 析构器，当一个对象将要被系统回收之时调用的方法。\n",
    "\n",
    "> Python 采用自动引用计数（ARC）方式来回收对象所占用的空间，当程序中有一个变量引用该 Python 对象时，Python 会自动保证该对象引用计数为 1；当程序中有两个变量引用该 Python 对象时，Python 会自动保证该对象引用计数为 2，依此类推，如果一个对象的引用计数变成了 0，则说明程序中不再有变量引用该对象，表明程序不再需要该对象，因此 Python 就会回收该对象。\n",
    ">\n",
    "> 大部分时候，Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况，比如对象 a 持有一个实例变量引用对象 b，而对象 b 又持有一个实例变量引用对象 a，此时两个对象的引用计数都是 1，而实际上程序已经不再有变量引用它们，系统应该回收它们，此时 Python 的垃圾回收器就可能没那么快，要等专门的循环垃圾回收器（Cyclic Garbage Collector）来检测并回收这种引用循环。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "into C __init__\n",
      "into C __del__\n"
     ]
    }
   ],
   "source": [
    "class C(object):\n",
    "    def __init__(self):\n",
    "        print('into C __init__')\n",
    "\n",
    "    def __del__(self):\n",
    "        print('into C __del__')\n",
    "\n",
    "\n",
    "c1 = C()\n",
    "# into C __init__\n",
    "c2 = c1\n",
    "c3 = c2\n",
    "del c3\n",
    "del c2\n",
    "del c1\n",
    "# into C __del__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "- `__str__(self)`:\n",
    "    - 当你打印一个对象的时候，触发`__str__`\n",
    "    - 当你使用`%s`格式化的时候，触发`__str__`\n",
    "    - `str`强转数据类型的时候，触发`__str__`\n",
    "\n",
    "- `__repr__(self)`：\n",
    "    - `repr`是`str`的备胎\n",
    "    - 有`__str__`的时候执行`__str__`,没有实现`__str__`的时候，执行`__repr__`\n",
    "    - `repr(obj)`内置函数对应的结果是`__repr__`的返回值\n",
    "    - 当你使用`%r`格式化的时候 触发`__repr__`\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "名字是:汤姆 , 年龄是:30\n",
      "名字是:汤姆 , 年龄是:30\n",
      "Cat:(汤姆,30)\n",
      "汤姆在吃鱼....\n",
      "名字是:汤姆, 年龄是:30\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "    \"\"\"定义一个猫类\"\"\"\n",
    "\n",
    "    def __init__(self, new_name, new_age):\n",
    "        \"\"\"在创建完对象之后 会自动调用, 它完成对象的初始化的功能\"\"\"\n",
    "        self.name = new_name\n",
    "        self.age = new_age\n",
    "\n",
    "    def __str__(self):\n",
    "        \"\"\"返回一个对象的描述信息\"\"\"\n",
    "        return \"名字是:%s , 年龄是:%d\" % (self.name, self.age)\n",
    "        \n",
    "    def __repr__(self):\n",
    "        \"\"\"返回一个对象的描述信息\"\"\"\n",
    "        return \"Cat:(%s,%d)\" % (self.name, self.age)\n",
    "\n",
    "    def eat(self):\n",
    "        print(\"%s在吃鱼....\" % self.name)\n",
    "\n",
    "    def drink(self):\n",
    "        print(\"%s在喝可乐...\" % self.name)\n",
    "\n",
    "    def introduce(self):\n",
    "        print(\"名字是:%s, 年龄是:%d\" % (self.name, self.age))\n",
    "\n",
    "\n",
    "# 创建了一个对象\n",
    "tom = Cat(\"汤姆\", 30)\n",
    "print(tom)  # 名字是:汤姆 , 年龄是:30\n",
    "print(str(tom)) # 名字是:汤姆 , 年龄是:30\n",
    "print(repr(tom))  # Cat:(汤姆,30)\n",
    "tom.eat()  # 汤姆在吃鱼....\n",
    "tom.introduce()  # 名字是:汤姆, 年龄是:30"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "`__str__(self)` 的返回结果可读性强。也就是说，`__str__` 的意义是得到便于人们阅读的信息，就像下面的 '2019-10-11' 一样。\n",
    "\n",
    "`__repr__(self)` 的返回结果应更准确。怎么说，`__repr__` 存在的目的在于调试，便于开发者使用。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2021-08-25\n",
      "datetime.date(2021, 8, 25)\n",
      "2021-08-25\n",
      "datetime.date(2021, 8, 25)\n"
     ]
    }
   ],
   "source": [
    "import datetime\n",
    "\n",
    "today = datetime.date.today()\n",
    "print(str(today))  # 2021-08-25\n",
    "print(repr(today))  # datetime.date(2019, 10, 11)\n",
    "print('%s' %today)  # 2019-10-11\n",
    "print('%r' %today)  # datetime.date(2019, 10, 11)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 算术运算符\n",
    "\n",
    "类型工厂函数，指的是“不通过类而是通过函数来创建对象”。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'builtin_function_or_method'>\n",
      "<class 'builtin_function_or_method'>\n",
      "<class 'type'>\n",
      "<class 'type'>\n",
      "<class 'type'>\n",
      "<class 'type'>\n",
      "123\n",
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "class C:\n",
    "    pass\n",
    "\n",
    "\n",
    "print(type(len))  # <class 'builtin_function_or_method'>\n",
    "print(type(dir))  # <class 'builtin_function_or_method'>\n",
    "print(type(int))  # <class 'type'>\n",
    "print(type(list))  # <class 'type'>\n",
    "print(type(tuple))  # <class 'type'>\n",
    "print(type(C))  # <class 'type'>\n",
    "print(int('123'))  # 123\n",
    "\n",
    "# 这个例子中list工厂函数把一个元祖对象加工成了一个列表对象。\n",
    "print(list((1, 2, 3)))  # [1, 2, 3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__add__(self, other)`定义加法的行为：`+`\n",
    "- `__sub__(self, other)`定义减法的行为：`-`\n",
    "\n",
    "【例子】\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "高为 10  重为 5\n",
      "高为 20  重为 10\n",
      "高为 10  重为 5\n",
      "高为 30  重为 15\n"
     ]
    }
   ],
   "source": [
    "class MyClass:\n",
    "\n",
    "    def __init__(self, height, weight):\n",
    "        self.height = height\n",
    "        self.weight = weight\n",
    "\n",
    "    # 两个对象的高相加，重相加.返回一个新的类\n",
    "    def __add__(self, others):\n",
    "        return MyClass(self.height + others.height, self.weight + others.weight)\n",
    "\n",
    "    # 两个对象的高相减，重相减.返回一个新的类\n",
    "    def __sub__(self, others):\n",
    "        return MyClass(self.height - others.height, self.weight - others.weight)\n",
    "\n",
    "    # 说一下自己的参数\n",
    "    def intro(self):\n",
    "        print(\"高为\", self.height, \" 重为\", self.weight)\n",
    "\n",
    "\n",
    "def main():\n",
    "    a = MyClass(height=10, weight=5)\n",
    "    a.intro()\n",
    "\n",
    "    b = MyClass(height=20, weight=10)\n",
    "    b.intro()\n",
    "\n",
    "    c = b - a\n",
    "    c.intro()\n",
    "\n",
    "    d = a + b\n",
    "    d.intro()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n",
    "\n",
    "# 高为 10  重为 5\n",
    "# 高为 20  重为 10\n",
    "# 高为 10  重为 5\n",
    "# 高为 30  重为 15"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__mul__(self, other)`定义乘法的行为：`*`\n",
    "- `__truediv__(self, other)`定义真除法的行为：`/`\n",
    "- `__floordiv__(self, other)`定义整数除法的行为：`//`\n",
    "- `__mod__(self, other)` 定义取模算法的行为：`%`\n",
    "- `__divmod__(self, other)`定义当被 `divmod()` 调用时的行为\n",
    "- `divmod(a, b)`把除数和余数运算结果结合起来，返回一个包含商和余数的元组`(a // b, a % b)`。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 1)\n",
      "(4, 0)\n"
     ]
    }
   ],
   "source": [
    "print(divmod(7, 2))  # (3, 1)\n",
    "print(divmod(8, 2))  # (4, 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__pow__(self, other[, module])`定义当被 `power()` 调用或 `**` 运算时的行为\n",
    "- `__lshift__(self, other)`定义按位左移位的行为：`<<`\n",
    "- `__rshift__(self, other)`定义按位右移位的行为：`>>`\n",
    "- `__and__(self, other)`定义按位与操作的行为：`&`\n",
    "- `__xor__(self, other)`定义按位异或操作的行为：`^`\n",
    "- `__or__(self, other)`定义按位或操作的行为：`|`\n",
    "\n",
    "## 反算术运算符\n",
    "\n",
    "反运算魔方方法，与算术运算符保持一一对应，不同之处就是反运算的魔法方法多了一个“r”。当文件左操作不支持相应的操作时被调用。\n",
    "\n",
    "- `__radd__(self, other)`定义加法的行为：`+`\n",
    "- `__rsub__(self, other)`定义减法的行为：`-`\n",
    "- `__rmul__(self, other)`定义乘法的行为：`*`\n",
    "- `__rtruediv__(self, other)`定义真除法的行为：`/`\n",
    "- `__rfloordiv__(self, other)`定义整数除法的行为：`//`\n",
    "- `__rmod__(self, other)` 定义取模算法的行为：`%`\n",
    "- `__rdivmod__(self, other)`定义当被 divmod() 调用时的行为\n",
    "- `__rpow__(self, other[, module])`定义当被 power() 调用或 `**` 运算时的行为\n",
    "- `__rlshift__(self, other)`定义按位左移位的行为：`<<`\n",
    "- `__rrshift__(self, other)`定义按位右移位的行为：`>>`\n",
    "- `__rand__(self, other)`定义按位与操作的行为：`&`\n",
    "- `__rxor__(self, other)`定义按位异或操作的行为：`^`\n",
    "- `__ror__(self, other)`定义按位或操作的行为：`|`\n",
    "\n",
    "`a + b`\n",
    "\n",
    "这里加数是`a`，被加数是`b`，因此是`a`主动，反运算就是如果`a`对象的`__add__()`方法没有实现或者不支持相应的操作，那么 Python 就会调用`b`的`__radd__()`方法。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n",
      "-2\n"
     ]
    }
   ],
   "source": [
    "class Nint(int):\n",
    "    def __radd__(self, other):\n",
    "        return int.__sub__(other, self) # 注意 self 在后面\n",
    "\n",
    "\n",
    "a = Nint(5)\n",
    "b = Nint(3)\n",
    "print(a + b)  # 8\n",
    "print(1 + b)  # -2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 增量赋值运算符\n",
    "\n",
    "- `__iadd__(self, other)`定义赋值加法的行为：`+=`\n",
    "- `__isub__(self, other)`定义赋值减法的行为：`-=`\n",
    "- `__imul__(self, other)`定义赋值乘法的行为：`*=`\n",
    "- `__itruediv__(self, other)`定义赋值真除法的行为：`/=`\n",
    "- `__ifloordiv__(self, other)`定义赋值整数除法的行为：`//=`\n",
    "- `__imod__(self, other)`定义赋值取模算法的行为：`%=`\n",
    "- `__ipow__(self, other[, modulo])`定义赋值幂运算的行为：`**=`\n",
    "- `__ilshift__(self, other)`定义赋值按位左移位的行为：`<<=`\n",
    "- `__irshift__(self, other)`定义赋值按位右移位的行为：`>>=`\n",
    "- `__iand__(self, other)`定义赋值按位与操作的行为：`&=`\n",
    "- `__ixor__(self, other)`定义赋值按位异或操作的行为：`^=`\n",
    "- `__ior__(self, other)`定义赋值按位或操作的行为：`|=`\n",
    "\n",
    "\n",
    "## 一元运算符\n",
    "- `__neg__(self)`定义负号的行为：`-x`\n",
    "- `__pos__(self)`定义正号的行为：`+x`\n",
    "- `__abs__(self)`定义当被`abs()`调用时的行为\n",
    "- `__invert__(self)`定义按位求反的行为：`~x`\n",
    "\n",
    "## 属性访问\n",
    "\n",
    "\n",
    "\n",
    "- `__getattr__(self, name)`: 定义当用户试图获取一个不存在的属性时的行为。\n",
    "- `__getattribute__(self, name)`：定义当该类的属性被访问时的行为（先调用该方法，查看是否存在该属性，若不存在，接着去调用`__getattr__`）。\n",
    "- `__setattr__(self, name, value)`：定义当一个属性被设置时的行为。\n",
    "- `__delattr__(self, name)`：定义当一个属性被删除时的行为。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__getattribute__\n",
      "__getattr__\n",
      "__setattr__\n",
      "__delattr__\n"
     ]
    }
   ],
   "source": [
    "class C:\n",
    "    def __getattribute__(self, item):\n",
    "        print('__getattribute__')\n",
    "        return super().__getattribute__(item)\n",
    "\n",
    "    def __getattr__(self, item):\n",
    "        print('__getattr__')\n",
    "\n",
    "    def __setattr__(self, key, value):\n",
    "        print('__setattr__')\n",
    "        super().__setattr__(key, value)\n",
    "\n",
    "    def __delattr__(self, item):\n",
    "        print('__delattr__')\n",
    "        super().__delattr__(item)\n",
    "\n",
    "\n",
    "c = C()\n",
    "c.x\n",
    "# __getattribute__\n",
    "# __getattr__\n",
    "\n",
    "c.x = 1\n",
    "# __setattr__\n",
    "\n",
    "del c.x\n",
    "# __delattr__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "## 描述符\n",
    "\n",
    "描述符就是将某种特殊类型的类的实例指派给另一个类的属性。\n",
    "- `__get__(self, instance, owner)`用于访问属性，它返回属性的值。\n",
    "- `__set__(self, instance, value)`将在属性分配操作中调用，不返回任何内容。\n",
    "- `__del__(self, instance)`控制删除操作，不返回任何内容。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__get__ <__main__.MyDecriptor object at 0x7f2720675080> <__main__.Test object at 0x7f2720675630> <class '__main__.Test'>\n",
      "__set__ <__main__.MyDecriptor object at 0x7f2720675080> <__main__.Test object at 0x7f2720675630> x-man\n",
      "__delete__ <__main__.MyDecriptor object at 0x7f2720675080> <__main__.Test object at 0x7f2720675630>\n"
     ]
    }
   ],
   "source": [
    "class MyDecriptor:\n",
    "    def __get__(self, instance, owner):\n",
    "        print('__get__', self, instance, owner)\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        print('__set__', self, instance, value)\n",
    "\n",
    "    def __delete__(self, instance):\n",
    "        print('__delete__', self, instance)\n",
    "\n",
    "\n",
    "class Test:\n",
    "    x = MyDecriptor()\n",
    "\n",
    "\n",
    "t = Test()\n",
    "t.x\n",
    "# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'>\n",
    "\n",
    "t.x = 'x-man'\n",
    "# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man\n",
    "\n",
    "del t.x\n",
    "# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 定制序列\n",
    "\n",
    "协议（Protocols）与其它编程语言中的接口很相似，它规定你哪些方法必须要定义。然而，在 Python 中的协议就显得不那么正式。事实上，在 Python 中，协议更像是一种指南。\n",
    "\n",
    "**容器类型的协议**\n",
    "\n",
    "- 如果说你希望定制的容器是不可变的话，你只需要定义`__len__()`和`__getitem__()`方法。\n",
    "- 如果你希望定制的容器是可变的话，除了`__len__()`和`__getitem__()`方法，你还需要定义`__setitem__()`和`__delitem__()`两个方法。\n",
    "\n",
    "\n",
    "【例子】编写一个不可改变的自定义列表，要求记录列表中每个元素被访问的次数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "6\n",
      "7\n",
      "{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}\n",
      "{0: 0, 1: 1, 2: 1, 3: 0, 4: 0}\n"
     ]
    }
   ],
   "source": [
    "class CountList:\n",
    "    def __init__(self, *args):\n",
    "        self.values = [x for x in args]\n",
    "        self.count = {}.fromkeys(range(len(self.values)), 0)\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.values)\n",
    "\n",
    "    def __getitem__(self, item):\n",
    "        self.count[item] += 1\n",
    "        return self.values[item]\n",
    "\n",
    "\n",
    "c1 = CountList(1, 3, 5, 7, 9)\n",
    "c2 = CountList(2, 4, 6, 8, 10)\n",
    "print(c1[1])  # 3\n",
    "print(c2[2])  # 6\n",
    "print(c1[1] + c2[1])  # 7\n",
    "\n",
    "print(c1.count)\n",
    "# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}\n",
    "\n",
    "print(c2.count)\n",
    "# {0: 0, 1: 1, 2: 1, 3: 0, 4: 0}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- `__len__(self)`定义当被`len()`调用时的行为（返回容器中元素的个数）。\n",
    "- `__getitem__(self, key)`定义获取容器中元素的行为，相当于`self[key]`。\n",
    "- `__setitem__(self, key, value)`定义设置容器中指定元素的行为，相当于`self[key] = value`。\n",
    "- `__delitem__(self, key)`定义删除容器中指定元素的行为，相当于`del self[key]`。\n",
    "\n",
    "\n",
    "【例子】编写一个可改变的自定义列表，要求记录列表中每个元素被访问的次数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "6\n",
      "15\n",
      "{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}\n",
      "{0: 0, 1: 0, 2: 2, 3: 0, 4: 0}\n",
      "{0: 0, 1: 0, 2: 0, 3: 0}\n"
     ]
    }
   ],
   "source": [
    "class CountList:\n",
    "    def __init__(self, *args):\n",
    "        self.values = [x for x in args]\n",
    "        self.count = {}.fromkeys(range(len(self.values)), 0)\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.values)\n",
    "\n",
    "    def __getitem__(self, item):\n",
    "        self.count[item] += 1\n",
    "        return self.values[item]\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        self.values[key] = value\n",
    "\n",
    "    def __delitem__(self, key):\n",
    "        del self.values[key]\n",
    "        for i in range(0, len(self.values)):\n",
    "            if i >= key:\n",
    "                self.count[i] = self.count[i + 1]\n",
    "        self.count.pop(len(self.values))\n",
    "\n",
    "\n",
    "c1 = CountList(1, 3, 5, 7, 9)\n",
    "c2 = CountList(2, 4, 6, 8, 10)\n",
    "print(c1[1])  # 3\n",
    "print(c2[2])  # 6\n",
    "c2[2] = 12\n",
    "print(c1[1] + c2[2])  # 15\n",
    "print(c1.count)\n",
    "# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}\n",
    "print(c2.count)\n",
    "# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}\n",
    "del c1[1]\n",
    "print(c1.count)\n",
    "# {0: 0, 1: 0, 2: 0, 3: 0}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 迭代器\n",
    "\n",
    "- 迭代是 Python 最强大的功能之一，是访问集合元素的一种方式。\n",
    "- 迭代器是一个可以记住遍历的位置的对象。\n",
    "- 迭代器对象从集合的第一个元素开始访问，直到所有的元素被访问完结束。\n",
    "- 迭代器只能往前不会后退。\n",
    "- 字符串，列表或元组对象都可用于创建迭代器：\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "l\n",
      "s\n",
      "g\n",
      "o\n",
      "g\n",
      "r\n",
      "o\n",
      "u\n",
      "p\n",
      "l\n",
      "s\n",
      "g\n",
      "o\n",
      "g\n",
      "r\n",
      "o\n",
      "u\n",
      "p\n"
     ]
    }
   ],
   "source": [
    "string = 'lsgogroup'\n",
    "for c in string:\n",
    "    print(c)\n",
    "\n",
    "'''\n",
    "l\n",
    "s\n",
    "g\n",
    "o\n",
    "g\n",
    "r\n",
    "o\n",
    "u\n",
    "p\n",
    "'''\n",
    "\n",
    "for c in iter(string):\n",
    "    print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B -> 百度\n",
      "A -> 阿里\n",
      "T -> 腾讯\n",
      "B -> 百度\n",
      "A -> 阿里\n",
      "T -> 腾讯\n"
     ]
    }
   ],
   "source": [
    "links = {'B': '百度', 'A': '阿里', 'T': '腾讯'}\n",
    "for each in links:\n",
    "    print('%s -> %s' % (each, links[each]))\n",
    "    \n",
    "'''\n",
    "B -> 百度\n",
    "A -> 阿里\n",
    "T -> 腾讯\n",
    "'''\n",
    "\n",
    "for each in iter(links):\n",
    "    print('%s -> %s' % (each, links[each]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- 迭代器有两个基本的方法：`iter()` 和 `next()`。\n",
    "- `iter(object)` 函数用来生成迭代器。\n",
    "- `next(iterator[, default])` 返回迭代器的下一个项目。\n",
    "- `iterator` -- 可迭代对象\n",
    "- `default` -- 可选，用于设置在没有下一个元素时返回该默认值，如果不设置，又没有下一个元素则会触发 `StopIteration` 异常。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B\n",
      "A\n",
      "T\n",
      "B\n",
      "A\n",
      "T\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-16-682f811edce7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     17\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# A\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# T\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# StopIteration\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "links = {'B': '百度', 'A': '阿里', 'T': '腾讯'}\n",
    "\n",
    "it = iter(links)\n",
    "while True:\n",
    "    try:\n",
    "        each = next(it)\n",
    "    except StopIteration:\n",
    "        break\n",
    "    print(each)\n",
    "\n",
    "# B\n",
    "# A\n",
    "# T\n",
    "\n",
    "it = iter(links)\n",
    "print(next(it))  # B\n",
    "print(next(it))  # A\n",
    "print(next(it))  # T\n",
    "print(next(it))  # StopIteration"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "把一个类作为一个迭代器使用需要在类中实现两个魔法方法 `__iter__()` 与 `__next__()` 。\n",
    "\n",
    "- `__iter__(self)`定义当迭代容器中的元素的行为，返回一个特殊的迭代器对象， 这个迭代器对象实现了 `__next__()` 方法并通过 `StopIteration` 异常标识迭代的完成。\n",
    "- `__next__()` 返回下一个迭代器对象。\n",
    "- `StopIteration` 异常用于标识迭代的完成，防止出现无限循环的情况，在 `__next__()` 方法中我们可以设置在完成指定循环次数后触发 `StopIteration` 异常来结束迭代。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1 2 3 5 8 13 21 34 55 89 "
     ]
    }
   ],
   "source": [
    "class Fibs:\n",
    "    def __init__(self, n=10):\n",
    "        self.a = 0\n",
    "        self.b = 1\n",
    "        self.n = n\n",
    "\n",
    "    def __iter__(self):\n",
    "        return self\n",
    "\n",
    "    def __next__(self):\n",
    "        self.a, self.b = self.b, self.a + self.b\n",
    "        if self.a > self.n:\n",
    "            raise StopIteration\n",
    "        return self.a\n",
    "\n",
    "\n",
    "fibs = Fibs(100)\n",
    "for each in fibs:\n",
    "    print(each, end=' ')\n",
    "\n",
    "# 1 1 2 3 5 8 13 21 34 55 89"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "<b>4.10 生成器</b>\n",
    "\n",
    "- 在 Python 中，使用了 `yield` 的函数被称为生成器（generator）。\n",
    "- 跟普通函数不同的是，生成器是一个返回迭代器的函数，只能用于迭代操作，更简单点理解生成器就是一个迭代器。\n",
    "- 在调用生成器运行的过程中，每次遇到 `yield` 时函数会暂停并保存当前所有的运行信息，返回 `yield` 的值, 并在下一次执行 `next()` 方法时从当前位置继续运行。\n",
    "- 调用一个生成器函数，返回的是一个迭代器对象。\n",
    "\n",
    "【例子】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "生成器执行！\n",
      "1\n",
      "2\n",
      "生成器执行！\n",
      "1\n",
      "2\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-56bfa60ce8de>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     15\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmyG\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmyG\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# StopIteration\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "def myGen():\n",
    "    print('生成器执行！')\n",
    "    yield 1\n",
    "    yield 2\n",
    "    \n",
    "myG = myGen()\n",
    "for each in myG:\n",
    "    print(each)\n",
    "\n",
    "'''\n",
    "生成器执行！\n",
    "1\n",
    "2\n",
    "'''\n",
    "\n",
    "myG = myGen()\n",
    "print(next(myG))  \n",
    "# 生成器执行！\n",
    "# 1\n",
    "\n",
    "print(next(myG))  # 2\n",
    "print(next(myG))  # StopIteration"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "【例子】用生成器实现斐波那契数列。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1 2 3 5 8 13 21 34 55 89 "
     ]
    }
   ],
   "source": [
    "def libs(n):\n",
    "    a = 0\n",
    "    b = 1\n",
    "    while True:\n",
    "        a, b = b, a + b\n",
    "        if a > n:\n",
    "            return\n",
    "        yield a\n",
    "\n",
    "\n",
    "for each in libs(100):\n",
    "    print(each, end=' ')\n",
    "\n",
    "# 1 1 2 3 5 8 13 21 34 55 89"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "目录",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
